﻿/* 
 * Copyright (c) 2011 Jeff Zhang
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of 
 * this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation the rights to use, 
 * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 
 * and to permit persons to whom the Software is furnished to do so, 
 * subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included 
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

using System;
using System.Collections.Generic;
using System.Text;

namespace Json {
    /// <summary>
    /// IJsonObject
    /// </summary>
    public interface IJsonObject {
        /// <summary>
        /// Get the value of json. that may be <see cref="Json.Number"/>,<![CDATA[IList<IJsonObject>]]>, DateTime, 
        /// Boolean, String, <see cref="Json.InternalDictionary"/>, or object.
        /// </summary>
        /// <seealso cref="Json.Number"/>
        /// <seealso cref="Json.InternalDictionary"/>
        object Value { get; }

        /// <summary>
        /// Get the type of json.
        /// </summary>
        /// <seealso cref="Json.DataType"/>
        DataType Type { get; }

        /// <summary>
        /// Get the length of json value.
        /// It is available when is <see cref="Json.JsonDictionary"/> or <see cref="Json.JsonArray"/>.
        /// Unless will throw <see cref="Json.IllegalOperationException"/>.
        /// </summary>
        /// <exception cref="Json.IllegalOperationException">If the class is not <see cref="Json.JsonDictionary"/>
        /// or <see cref="Json.JsonArray"/>, accessing this property will occur <see cref="Json.IllegalOperationException"/>.  
        /// </exception>
        /// <seealso cref="Json.JsonDictionary"/>
        /// <seealso cref="Json.JsonArray"/>
        /// <seealso cref="Json.IllegalOperationException"/>
        int Length { get; }

        /// <summary>
        /// Get a json object, if the type is dictionary, 
        /// or throw <see cref="Json.IllegalOperationException"/>.
        /// </summary>
        /// <param name="key"></param>
        /// <exception cref="Json.IllegalOperationException">
        /// If the class is not <see cref="Json.JsonDictionary"/>, 
        /// accessing this property will occur <see cref="Json.IllegalOperationException"/>.  
        /// </exception>
        /// <returns>Return a IJsonObject.</returns>
        /// <seealso cref="Json.JsonDictionary"/>
        /// <seealso cref="Json.IJsonObject"/>
        /// <seealso cref="Json.IllegalOperationException"/>
        IJsonObject this[string key] { get; set; }

        /// <summary>
        /// Get a json object, if the type is dictionary or array.
        /// or throw <see cref="Json.IllegalOperationException"/>.
        /// </summary>
        /// <param name="index"></param>
        /// <seealso cref="Json.IJsonObject"/>
        /// <exception cref="Json.IllegalOperationException"></exception>
        /// <returns>The json interface.</returns>
        IJsonObject this[int index] { get; set; }

        /// <summary>
        /// Return json object string.
        /// </summary>
        /// <returns>The json string.</returns>
        string ToJson();

        /// <summary>
        /// Return json byte format.
        /// </summary>
        /// <returns>The byte array.</returns>
        byte[] ToBytes();
    }


    public interface IJsonObject<T> {
        T Value { get; }
    }

    public abstract class JsonArray<T> : JsonObject<IList<T>>, IEnumerable<T>
        where T : IJsonObject {

        protected JsonArray() : base(new List<T>()) { }
        protected JsonArray(int capacity) : base(new List<T>(capacity)) { }
        protected JsonArray(IEnumerable<T> values) : base(new List<T>(values)) { }

        public abstract DataType Type { get; }

        public override int Length {
            get { return Value.Count; }
        }

        public void Add(T value) {
            Value.Add(value);
        }

        public override IJsonObject this[string key] {
            get {
                throw new IllegalOperationException();
            }
            set {
                throw new IllegalOperationException();
            }
        }

        public override IJsonObject this[int index] {
            get {
                if (index < 0 || index > Value.Count) {
                    throw new ArgumentOutOfRangeException("index");
                }

                return Value[index];
            }
            set {
                if (index < 0 || index > Value.Count) {
                    throw new ArgumentOutOfRangeException("index");
                }

                if ((value.Type & this.Type) == this.Type) {
                    Value[index] = (T)value;
                } else {
                    throw new ArgumentOutOfRangeException("index");
                }
            }
        }

        public IEnumerator<T> GetEnumerator() {
            return Value.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            return Value.GetEnumerator();
        }
    }
}
